% run_simulation_for_viz.m
% 运行一次完整的内存分配模拟，并为可视化采集详细的历史数据。
% (版本 24: 修正了 vertcat 维度不一致的错误)

function history = run_simulation_for_viz(solution, graph, code_map, l1_cap, ub_cap, l0a_cap, l0b_cap, l0c_cap)
    
    fprintf('\n--- 开始为可视化采集数据 ---\n');

    % --- 1. 初始化 ---
    num_steps = length(solution.final_schedule);

    % =====================================================================
    % [Perf-Option 1/2] 可视化采样步长与是否记录块明细（大幅降低内存与绘制成本）
    % - 将步长设置为 >1 可减少记录点数量（例如 5 或 10）
    % - 关闭 record_allocated_blocks 可避免保存逐步的块列表（甘特图将不可用）
    % =====================================================================
    viz_step_stride = 10;                 % 每隔多少步记录一次（1 表示逐步记录）
    record_allocated_blocks = true;      % 如不需要甘特图，设为 false 可显著提速

    % 计算采样步列表，并确保包含最后一步
    record_steps = 1:viz_step_stride:num_steps;
    if isempty(record_steps) || record_steps(end) ~= num_steps
        record_steps(end+1) = num_steps; %#ok<AGROW>
    end
    num_record_steps = length(record_steps);
    
    % 将容量存入Map以便于访问
    capacities = containers.Map('KeyType','int32','ValueType','double');
    if code_map.L1 > 0, capacities(code_map.L1) = l1_cap; end
    if code_map.UB > 0, capacities(code_map.UB) = ub_cap; end
    if code_map.L0A > 0, capacities(code_map.L0A) = l0a_cap; end
    if code_map.L0B > 0, capacities(code_map.L0B) = l0b_cap; end
    if code_map.L0C > 0, capacities(code_map.L0C) = l0c_cap; end

    % 预分配历史记录结构体以提高效率
    history = struct();
    % 为了让下游绘图代码保持简单，一律将横轴步长设置为 1:num_record_steps
    % 若需要真实的原始步号，可在此另外存入 history.recorded_step_numbers = record_steps;
    history.steps = 1:num_record_steps;
    
    type_codes = cell2mat(struct2cell(code_map));
    max_type_code = max(type_codes(type_codes > 0));

    history.memory_usage = nan(num_record_steps, max_type_code);
    history.num_free_blocks = nan(num_record_steps, max_type_code);
    history.max_free_block_size = nan(num_record_steps, max_type_code);
    if record_allocated_blocks
        history.allocated_blocks = cell(num_record_steps, max_type_code);
    else
        history.allocated_blocks = [];
    end
    history.buffer_lifetimes = containers.Map('KeyType','int32','ValueType','any');

    % 模拟所需的内部状态变量
    cache_managers = containers.Map('KeyType', 'int32', 'ValueType', 'any');
    for tc = keys(capacities)
        cache_managers(tc{1}) = cm_create(capacities(tc{1}));
    end
    % 缓存类型键集合，避免每步重复 keys(...) 分配
    tc_keys = keys(cache_managers);
    
    buf_info_map = containers.Map('KeyType', 'int32', 'ValueType', 'any');
    for i = 1:length(graph.nodes)
        node = graph.nodes(i);
        if isempty(node.id), continue; end
        if node.op_code == code_map.ALLOC && ~isempty(node.buf_id) && ~isempty(node.size)
            buf_info_map(node.buf_id) = {node.size, node.type_code};
        end
    end
    
    active_buffers = containers.Map('KeyType', 'int32', 'ValueType', 'any'); % key: buf_id, val: {start_step, type_code}

    % --- 2. 运行模拟并记录数据 ---
    rec_ptr = 1;                        % 指向下一个需要记录的原始步号在 record_steps 中的位置
    next_record_step = record_steps(rec_ptr);
    rec_idx = 0;                        % 已写入的采样记录索引（1..num_record_steps）

    for step = 1:num_steps
        node_id = solution.final_schedule(step);
        node = graph.nodes(node_id);

        % 执行内存操作来更新 cache_managers 状态
        if strcmp(node.op, 'SPILL_IN') || node.op_code == code_map.ALLOC
            buf_id = node.buf_id;
            if isempty(buf_id), continue; end
            
            if node.op_code == code_map.ALLOC
                offset = solution.memory_offsets(buf_id);
                size = node.size;
                type_code = node.type_code;
            else % SPILL_IN
                offset = solution.spill_map(node_id);
                info = buf_info_map(buf_id);
                size = info{1};
                type_code = info{2};
            end
            
            if ~isKey(cache_managers, type_code), continue; end

            manager = cache_managers(type_code);
            [manager, success] = cm_allocate_at(manager, size, buf_id, offset);
            if ~success, error('可视化模拟失败：在步骤 %d 发生冲突', step); end
            cache_managers(type_code) = manager;
            
            % 记录buffer生命周期开始
            active_buffers(buf_id) = {step, type_code};

        elseif node.op_code == code_map.FREE || strcmp(node.op, 'SPILL_OUT')
            if isempty(node.buf_id), continue; end
            buf_id = node.buf_id;
            
            if isKey(active_buffers, buf_id)
                info = active_buffers(buf_id);
                start_step = info{1};
                type_code = info{2};
                
                if ~isKey(cache_managers, type_code), continue; end
                
                manager = cache_managers(type_code);
                manager = cm_free(manager, buf_id);
                cache_managers(type_code) = manager;
                
                % 记录buffer生命周期结束
                remove(active_buffers, buf_id);
                if isKey(history.buffer_lifetimes, buf_id)
                    % *** 已修正: 使用 {{...}} 来进行正确的元胞数组追加 ***
                    history.buffer_lifetimes(buf_id) = [history.buffer_lifetimes(buf_id); {{start_step, step, type_code}}];
                else
                    history.buffer_lifetimes(buf_id) = {{start_step, step, type_code}};
                end
            end
        end

        % 仅在采样步进行记录（降低 I/O 与内存开销）
        if step == next_record_step
            rec_idx = rec_idx + 1;
            for k = 1:numel(tc_keys)
                tc = tc_keys{k};
                manager = cache_managers(tc);

                % 可选：记录已分配块（供甘特图使用）。关闭可显著降低内存占用
                if record_allocated_blocks
                    history.allocated_blocks{rec_idx, tc} = manager.allocated_blocks;
                end

                % 计算并记录统计数据
                total_alloc_size = sum([manager.allocated_blocks.size]);
                if manager.capacity > 0
                    history.memory_usage(rec_idx, tc) = total_alloc_size / manager.capacity;
                else
                    history.memory_usage(rec_idx, tc) = 0;
                end
                history.num_free_blocks(rec_idx, tc) = length(manager.free_blocks);
                if ~isempty(manager.free_blocks)
                    history.max_free_block_size(rec_idx, tc) = max([manager.free_blocks.size]);
                else
                    history.max_free_block_size(rec_idx, tc) = 0;
                end
            end

            % 推进到下一采样点
            if rec_ptr < num_record_steps
                rec_ptr = rec_ptr + 1;
                next_record_step = record_steps(rec_ptr);
            else
                next_record_step = inf; % 不再记录
            end
        end
    end
    
    % 处理在模拟结束时仍然活跃的buffer
    for buf_id_cell = keys(active_buffers)
        buf_id = buf_id_cell{1};
        info = active_buffers(buf_id);
        start_step = info{1};
        type_code = info{2};
        if isKey(history.buffer_lifetimes, buf_id)
            % *** 已修正: 使用 {{...}} 来进行正确的元胞数组追加 ***
            history.buffer_lifetimes(buf_id) = [history.buffer_lifetimes(buf_id); {{start_step, num_steps, type_code}}];
        else
            history.buffer_lifetimes(buf_id) = {{start_step, num_steps, type_code}};
        end
    end

    fprintf('数据采集完成。\n');
end